﻿using System;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;

using RaisingStudio.SmallProgram.Library.Internal;

namespace RaisingStudio.SmallProgram.Library
{
    [SmallProgramType]
    public static class Turtle
    {
        private static FrameworkElement _turtle;
        private const string _turtleName = "_turtle";
        private static RotateTransform _rotateTransform;
        private static bool _initialized = false;
        private static bool _isVisible = true;
        private static bool _penDown = true;

        private static double _angle = 0.0;
        public static Primitive Angle
        {
            get
            {
                return _angle;
            }
            set
            {
                _angle = (double)value;
                if (_rotateTransform != null)
                {
                    GraphicsWindow.Invoke(delegate
                    {
                        DoubleAnimation timeline = new DoubleAnimation
                        {
                            To = new double?(_angle),
                            Duration = new Duration(TimeSpan.FromMilliseconds(0.0))
                        };
                        _rotateTransform.BeginAnimation(RotateTransform.AngleProperty, timeline);
                    });
                }
            }
        }

        private static int _speed = 5;
        public static Primitive Speed
        {
            get
            {
                return _speed;
            }
            set
            {
                _speed = (int)value;
                if (_speed < 1)
                {
                    _speed = 1;
                }
                else if (_speed > 10)
                {
                    _speed = 10;
                }
            }
        }

        private static double _currentX = 320.0;
        public static Primitive X
        {
            get
            {
                return _currentX;
            }
            set
            {
                _currentX = (double)value;
                Shapes.Move("_turtle", _currentX, _currentY);
            }
        }

        private static double _currentY = 240.0;
        public static Primitive Y
        {
            get
            {
                return _currentY;
            }
            set
            {
                _currentY = (double)value;
                Shapes.Move("_turtle", _currentX, _currentY);
            }
        }


        private static void VerifyAccess()
        {
            GraphicsWindow.VerifyAccess();
            if (!_initialized)
            {
                _initialized = true;
                if (_isVisible)
                {
                    GraphicsWindow.Invoke(delegate
                    {
                        if (_turtle == null)
                        {
                            ImageSource source = new BitmapImage(SmallProgramApplication.GetResourceUri("Turtle.png"));
                            _turtle = new Image { Source = source, Margin = new Thickness(-8.0, -8.0, 0.0, 0.0), Height = 16.0, Width = 16.0 };
#if SILVERLIGHT
                            Canvas.SetZIndex(_turtle, 0xf4240);
#else
                            Panel.SetZIndex(_turtle, 0xf4240);
#endif
                            _rotateTransform = new RotateTransform { Angle = _angle, CenterX = 8.0, CenterY = 8.0 };
                            _turtle.RenderTransform = _rotateTransform;
                            _turtle.Width = 16.0;
                            _turtle.Height = 16.0;
                        }
                        Canvas.SetLeft(_turtle, _currentX);
                        Canvas.SetTop(_turtle, _currentY);
                        GraphicsWindow.AddShape("_turtle", _turtle);
                    });
                }
            }
        }
      
        public static void Show()
        {
            _isVisible = true;
            VerifyAccess();
        }
        public static void Hide()
        {
            if (_isVisible)
            {
                GraphicsWindow.VerifyAccess();
                GraphicsWindow.Invoke(delegate
                {
                    Shapes.Remove("_turtle");
                });
                _isVisible = false;
            }
        }


        public static void PenDown()
        {
            _penDown = true;
        }

        public static void PenUp()
        {
            _penDown = false;
        }


        public static void Turn(Primitive angle)
        {
            VerifyAccess();
            double animateTime = (double)RaisingStudio.SmallProgram.Library.Math.Abs((((double)angle.GetAsDecimal()) * 200.0) / ((double)(_speed * _speed)));
            if (_speed == 10)
            {
                animateTime = 5.0;
            }
            _angle += angle;
            GraphicsWindow.Invoke(delegate
            {
                DoubleAnimation timeline = new DoubleAnimation
                {
                    To = new double?(_angle),
                    Duration = new Duration(TimeSpan.FromMilliseconds(animateTime))
                };
                _rotateTransform.BeginAnimation(RotateTransform.AngleProperty, timeline);
            });
            WaitForReturn(animateTime);
        }

        public static void TurnLeft()
        {
            Turn(-90);
        }

        public static void TurnRight()
        {
            Turn(90);
        }


        public static void Move(Primitive distance)
        {
            VerifyAccess();
            double animateTime = (double)Math.Abs((((double)distance.GetAsDecimal()) * 320.0) / ((double)(_speed * _speed)));
            if (_speed == 10)
            {
                animateTime = 5.0;
            }
            double d = (_angle / 180.0) * Math.Pi;
            double newY = _currentY - (distance * System.Math.Cos(d));
            double newX = _currentX + (distance * System.Math.Sin(d));
            Shapes.Animate("_turtle", newX, newY, animateTime);
            if (_penDown)
            {
                GraphicsWindow.Invoke(delegate
                    {
                        string name = Shapes.GenerateNewName("_turtleLine");
                        Line shape = new Line
                        {
                            Name = name,
                            X1 = _currentX,
                            Y1 = _currentY,
                            Stroke = GraphicsWindow._pen.Brush,
                            StrokeThickness = GraphicsWindow._pen.Thickness
                        };
                        GraphicsWindow.AddShape(name, shape);
                        DoubleAnimation timeline = new DoubleAnimation
                        {
                            From = new double?(_currentX),
                            To = new double?(newX),
                            Duration = new Duration(TimeSpan.FromMilliseconds(animateTime))
                        };
                        DoubleAnimation animation2 = new DoubleAnimation
                        {
                            From = new double?(_currentY),
                            To = new double?(newY),
                            Duration = new Duration(TimeSpan.FromMilliseconds(animateTime))
                        };
                        shape.BeginAnimation(Line.X2Property, timeline);
                        shape.BeginAnimation(Line.Y2Property, animation2);
                    });
            }
            _currentX = newX;
            _currentY = newY;
            WaitForReturn(animateTime);
        }

        public static void MoveTo(Primitive x, Primitive y)
        {
            double d = (double)(((x - X) * (x - X)) + ((y - Y) * (y - Y)));
            if (d != 0.0)
            {
                double distance = System.Math.Sqrt(d);
                double yDistance = (double)(Y - y);
                double angleValue = (System.Math.Acos(yDistance / distance) * 180.0) / Math.Pi;
                if (x < X)
                {
                    angleValue = 360.0 - angleValue;
                }
                double angle = angleValue - ((double)(Angle % 360));
                if (angle > 180.0)
                {
                    angle -= 360.0;
                }
                Turn(angle);
                Move(distance);
            }
        }


        private static void WaitForReturn(double time)
        {
            AutoResetEvent autoResetEvent;
            if (!SmallProgramApplication.HasShutdown)
            {
                autoResetEvent = new AutoResetEvent(false);
                SmallProgramApplication.Invoke(delegate
                {
                    DispatcherTimer dt = new DispatcherTimer
                    {
                        Interval = TimeSpan.FromMilliseconds(time)
                    };
                    dt.Tick += delegate
                    {
                        autoResetEvent.Set();
                        dt.Stop();
                    };
                    dt.Start();
                });
                int millisecondsTimeout = 100;
                if (SmallProgramApplication.Dispatcher.CheckAccess())
                {
                    millisecondsTimeout = 10;
                }
                while (!autoResetEvent.WaitOne(millisecondsTimeout))
                {
                    if (SmallProgramApplication.HasShutdown)
                    {
                        break;
                    }
                    SmallProgramApplication.ClearDispatcherQueue();
                }
            }
        }
    }
}

